home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume10 / screen / part01 next >
Encoding:
Internet Message Format  |  1987-08-06  |  38.8 KB

  1. Path: uunet!rs
  2. From: rs@uunet.UU.NET (Rich Salz)
  3. Newsgroups: comp.sources.unix
  4. Subject: v10i095:  BSD multi-screen manager, Part01/02
  5. Message-ID: <787@uunet.UU.NET>
  6. Date: 7 Aug 87 11:14:56 GMT
  7. Organization: UUNET Communications Services, Arlington, VA
  8. Lines: 1699
  9. Approved: rs@uunet.UU.NET
  10.  
  11. Submitted-by: Oliver Laumann <seismo!tub!net>
  12. Posting-number: Volume 10, Issue 95
  13. Archive-name: screen/Part01
  14. Screen is a window manager that allows you to handle several independent
  15. screens (UNIX ttys) on a single physical terminal; each screen has its own
  16. set of processes connected to it (typically interactive shells).  Each
  17. virtual terminal created by "screen" emulates a DEC VT100 plus several ANSI
  18. X3.64 functions (including DEC VT102 features such as line and character
  19. deletion and insertion).
  20.  
  21.  
  22. #! /bin/sh
  23. # This is a shell archive, meaning:
  24. # 1. Remove everything above the #! /bin/sh line.
  25. # 2. Save the resulting text in a file.
  26. # 3. Execute the file with /bin/sh (not csh) to create the files:
  27. #    README
  28. #    Makefile
  29. #    screen.h
  30. #    ansi.c
  31. export PATH; PATH=/bin:$PATH
  32. echo shar: extracting "'README'" '(4634 characters)'
  33. if test -f 'README'
  34. then
  35.     echo shar: will not over-write existing file "'README'"
  36. else
  37. cat << \SHAR_EOF > 'README'
  38. "screen" is a window manager that allows you to handle several independent
  39. screens (UNIX ttys) on a single physical terminal; each screen has its own
  40. set of processes connected to it (typically interactive shells).  Each
  41. virtual terminal created by "screen" emulates a DEC VT100 plus several ANSI
  42. X3.64 functions (including DEC VT102 features such as line and character
  43. deletion and insertion).
  44.  
  45. Since "screen" uses pseudo-ttys, the select system call, and UNIX-domain
  46. sockets, it will not run under a system that does not include these
  47. features of 4.2 and 4.3 BSD UNIX.
  48.  
  49. If you want to get a quick idea how "screen" works but don't want to read
  50. the entire manual, do the following:
  51.  
  52.      -  call "screen" without arguments
  53.      -  wait for the shell prompt; execute some commands
  54.      -  type ^A ^C (Control-A followed by Control-C)
  55.      -  wait for the shell prompt; do something in the new window
  56.      -  type ^A ^A repeatedly to switch between the two windows
  57.      -  terminate the first shell ("screen" switches to the other window)
  58.      -  terminate the second shell
  59.  
  60. If you have got "vttest" (the VT100 test program from mod.sources) you
  61. may want to run it from within "screen" to verify that it correctly
  62. emulates a VT100 on your terminal (except for 132 column mode and
  63. double width/height characters, of course).
  64.  
  65. By the way, "screen" can be used to compensate for certain bugs of "real"
  66. VT100 terminals.  For instance, our 4.2 BSD version of mille(6) garbles
  67. the display on terminals of the VT100 family, but it works quite fine
  68. when it is invoked from within "screen".  In addition, "screen" enables
  69. you to use EMACS on terminals that are unable to generate Control-S and
  70. Control-Q from the keyboard or that require flow control using Control-S
  71. and Control-Q.  This is the reason why I have an alias like
  72.      alias emacs "screen emacs"
  73. in my .cshrc file.
  74.  
  75.  
  76. I have published a beta-test release of screen in the non-moderated
  77. sources newsgroup four months ago.  Since then I have received numerous
  78. suggestions for enhancements and improvements, many of which I have
  79. included in this release of "screen".  One person reported that screen
  80. does not work correctly on the Ann Arbor Guru-XL; I have not been able
  81. to track the problem down without having more detailed information than
  82. just the termcap entry.
  83.  
  84. Major changes between this and the beta-test release are:
  85.  
  86.     -  "screen" now creates an entry in /etc/utmp for each virtual
  87.        terminal
  88.     -  the owner of the tty file for a virtual terminal is set properly
  89.        (provided that "screen" is set-uid with owner root)
  90.     -  the -c option has been eliminated; you can now start a command
  91.        in a new window with "screen [cmd [args]]"
  92.     -  a (user-settable) notification can be displayed in the current
  93.        window's message line when the bell is rung in a background window
  94.     -  a "chdir" command can be placed into ".screenrc" to facilitate
  95.        creation of windows in specific directories
  96.     -  flow control can be disabled by means of a command line option or
  97.        a special termcap symbol (necessary for EMACS)
  98.     -  "C-a s" and "C-a q" can be used to send a Control-S or a Control-Q,
  99.        respectively (for certain terminals)
  100.     -  it is no longer necessary to specify the full pathname when a
  101.        command is started in a new window (and with the "bind" command)
  102.     -  "C-a ." can be used to write the current termcap entry to a file
  103.        (useful for "rlogin" which does not propagate TERMCAP)
  104.     -  "C-a C-t" displays status information (e.g. the load average and
  105.        the virtual terminal's parameters) in the message line
  106.     -  "C-a C-\" closes all windows and terminates screen
  107.  
  108. Before typing "make", you should have a look into the Makefile.
  109. If your system maintains a 4.3-BSD-style load average, add -DLOADAV to
  110. the compiler options.  In addition, you must set -DGETTTYENT if your
  111. system has the new format /etc/ttys and the getttyent(3) routines.
  112.  
  113. "screen" should be granted read and write access to /etc/utmp and, if
  114. -DLOADAV has been specified, read access to /vmunix and /dev/kmem.
  115. "screen" should be installed with set-uid and owner root to enable it
  116. to correctly change the owner of newly allocated virtual terminals.
  117. Failing to do this (e.g. if you fear a trojan horse) doesn't have any
  118. major disadvantages, except that w(1) and some other utilities may have
  119. some problems with the tty files of your virtual terminals.
  120.  
  121.  
  122. Have fun,
  123.     Oliver Laumann
  124.     Technical University of Berlin,
  125.     Communications and Operating Systems Research Group.
  126.  
  127.     net@tub.BITNET     US: pyramid!tub!net     Europe: unido!tub!net
  128.  
  129. SHAR_EOF
  130. if test 4634 -ne "`wc -c < 'README'`"
  131. then
  132.     echo shar: error transmitting "'README'" '(should have been 4634 characters)'
  133. fi
  134. fi # end of overwriting check
  135. echo shar: extracting "'Makefile'" '(832 characters)'
  136. if test -f 'Makefile'
  137. then
  138.     echo shar: will not over-write existing file "'Makefile'"
  139. else
  140. cat << \SHAR_EOF > 'Makefile'
  141. # The following options can be set:
  142. #
  143. # -DLOADAV    -- your system maintains a load average like 4.3 BSD does
  144. #                (an array of three doubles called _avenrun; it is read
  145. #                from /dev/kmem; _avenrun is taken from the namelist of
  146. #                /vmunix).  Don't set this on a Sun.
  147. # -DGETTTYENT -- your system has the new format /etc/ttys (like 4.3 BSD)
  148. #                and the getttyent(3) library functions.
  149. #
  150. # You should install as set-uid with owner root, so that it can read/write
  151. # /etc/utmp, read /dev/kmem, and chown/chmod the allocated pseudo-ttys.
  152.  
  153. OPTIONS= -DLOADAV
  154.  
  155. CFILES= screen.c ansi.c
  156. OFILES= screen.o ansi.o
  157.  
  158. screen: $(OFILES)
  159.     cc $(CFLAGS) -o screen $(OFILES) -ltermcap
  160.  
  161. screen.o: screen.c screen.h
  162.     cc $(OPTIONS) $(CFLAGS) -c screen.c
  163.  
  164. ansi.o: ansi.c screen.h
  165.     cc $(CFLAGS) -c ansi.c
  166. SHAR_EOF
  167. if test 832 -ne "`wc -c < 'Makefile'`"
  168. then
  169.     echo shar: error transmitting "'Makefile'" '(should have been 832 characters)'
  170. fi
  171. fi # end of overwriting check
  172. echo shar: extracting "'screen.h'" '(1495 characters)'
  173. if test -f 'screen.h'
  174. then
  175.     echo shar: will not over-write existing file "'screen.h'"
  176. else
  177. cat << \SHAR_EOF > 'screen.h'
  178. /* Copyright (c) 1987, Oliver Laumann, Technical University of Berlin.
  179.  * Not derived from licensed software.
  180.  *
  181.  * Permission is granted to freely use, copy, modify, and redistribute
  182.  * this software, provided that no attempt is made to gain profit from it,
  183.  * the author is not construed to be liable for any results of using the
  184.  * software, alterations are clearly marked as such, and this notice is
  185.  * not modified.
  186.  */
  187.  
  188. enum state_t {
  189.     LIT,         /* Literal input */
  190.     ESC,         /* Start of escape sequence */
  191.     STR,         /* Start of control string */
  192.     TERM,        /* ESC seen in control string */
  193.     CSI,         /* Reading arguments in "CSI Pn ; Pn ; ... ; XXX" */
  194. };
  195.  
  196. enum string_t {
  197.     NONE,
  198.     DCS,         /* Device control string */
  199.     OSC,         /* Operating system command */
  200.     APC,         /* Application program command */
  201.     PM,          /* Privacy message */
  202. };
  203.  
  204. #define MAXSTR       128
  205.  
  206. #define IOSIZE       80
  207.  
  208. struct win {
  209.     int wpid;
  210.     int ptyfd;
  211.     int aflag;
  212.     char outbuf[IOSIZE];
  213.     int outlen;
  214.     char cmd[MAXSTR];
  215.     char tty[MAXSTR];
  216.     int slot;
  217.     char **image;
  218.     char **attr;
  219.     int active;
  220.     int x, y;
  221.     char LocalAttr;
  222.     int saved;
  223.     int Saved_x, Saved_y;
  224.     char SavedLocalAttr;
  225.     int top, bot;
  226.     int wrap;
  227.     int origin;
  228.     int insert;
  229.     int keypad;
  230.     enum state_t state;
  231.     enum string_t StringType;
  232.     char string[MAXSTR];
  233.     char *stringp;
  234.     char *tabs;
  235.     int vbwait;
  236.     int bell;
  237. };
  238. SHAR_EOF
  239. if test 1495 -ne "`wc -c < 'screen.h'`"
  240. then
  241.     echo shar: error transmitting "'screen.h'" '(should have been 1495 characters)'
  242. fi
  243. fi # end of overwriting check
  244. echo shar: extracting "'ansi.c'" '(30215 characters)'
  245. if test -f 'ansi.c'
  246. then
  247.     echo shar: will not over-write existing file "'ansi.c'"
  248. else
  249. cat << \SHAR_EOF > 'ansi.c'
  250. /* Copyright (c) 1987, Oliver Laumann, Technical University of Berlin.
  251.  * Not derived from licensed software.
  252.  *
  253.  * Permission is granted to freely use, copy, modify, and redistribute
  254.  * this software, provided that no attempt is made to gain profit from it,
  255.  * the author is not construed to be liable for any results of using the
  256.  * software, alterations are clearly marked as such, and this notice is
  257.  * not modified.
  258.  */
  259.  
  260. char AnsiVersion[] = "ansi 1.0g 27-Apr-87";
  261.  
  262. #include <stdio.h>
  263. #include <sys/types.h>
  264. #include "screen.h"
  265.  
  266. #define A_SO     0x1    /* Standout mode */
  267. #define A_US     0x2    /* Underscore mode */
  268. #define A_BL     0x4    /* Blinking */
  269. #define A_BD     0x8    /* Bold mode */
  270. #define A_DI    0x10    /* Dim mode */
  271. #define A_RV    0x20    /* Reverse mode */
  272. #define A_MAX   0x20
  273.  
  274. /* Types of movement used by Goto() */
  275. enum move_t {
  276.     M_NONE,
  277.     M_UP,
  278.     M_DO,
  279.     M_LE,
  280.     M_RI,
  281.     M_RW,
  282.     M_CR,
  283. };
  284.  
  285. #define MAXARGS      64
  286. #define EXPENSIVE    1000
  287.  
  288. extern char *getenv(), *tgetstr(), *tgoto(), *malloc();
  289.  
  290. int rows, cols;
  291. int status;
  292. int flowctl;
  293. char Term[] = "TERM=screen";
  294. char Termcap[1024];
  295. char *blank;
  296. char PC;
  297. time_t TimeDisplayed;
  298.  
  299. static char tbuf[1024], tentry[1024];
  300. static char *tp = tentry;
  301. static char *TI, *TE, *BL, *VB, *BC, *CR, *NL, *CL, *IS, *CM;
  302. static char *US, *UE, *SO, *SE, *CE, *CD, *DO, *SR, *SF, *AL;
  303. static char *CS, *DL, *DC, *IC, *IM, *EI, *UP, *ND, *KS, *KE;
  304. static char *MB, *MD, *MH, *MR, *ME;
  305. static AM;
  306. static args[MAXARGS];
  307. static char GotArg[MAXARGS];
  308. static NumArgs;
  309. static char GlobalAttr, TmpAttr;
  310. static char *OldImage, *OldAttr;
  311. static last_x, last_y;
  312. static struct win *curr;
  313. static display = 1;
  314. static StrCost;
  315. static UPcost, DOcost, LEcost, NDcost, CRcost, IMcost, EIcost;
  316. static tcLineLen = 100;
  317. static char *null;
  318. static StatLen;
  319. static insert;
  320. static keypad;
  321.  
  322. static char *KeyCaps[] = {
  323.     "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7", "k8", "k9",
  324.     "kb", "kd", "kh", "kl", "ko", "kr", "ku",
  325.     0
  326. };
  327.  
  328. static char TermcapConst[] = "TERMCAP=\
  329. SC|screen|VT 100/ANSI X3.64 virtual terminal|\\\n\
  330. \t:DO=\\E[%dB:LE=\\E[%dD:RI=\\E[%dC:UP=\\E[%dA:bs:bt=\\E[Z:\\\n\
  331. \t:cd=\\E[J:ce=\\E[K:cl=\\E[2J\\E[H:cm=\\E[%i%d;%dH:ct=\\E[3g:\\\n\
  332. \t:do=\\E[B:nd=\\E[C:pt:rc=\\E8:rs=\\Ec:sc=\\E7:st=\\EH:up=\\E[A:";
  333.  
  334. InitTerm () {
  335.     register char *s;
  336.  
  337.     if ((s = getenv ("TERM")) == 0)
  338.     Msg (0, "No TERM in environment.");
  339.     if (tgetent (tbuf, s) != 1)
  340.     Msg (0, "Cannot find termcap entry for %s.", s);
  341.     cols = tgetnum ("co");
  342.     rows = tgetnum ("li");
  343.     if (cols <= 0)
  344.     cols = 80;
  345.     if (rows <= 0)
  346.     rows = 24;
  347.     if (tgetflag ("hc"))
  348.     Msg (0, "You can't run screen on a hardcopy terminal.");
  349.     if (tgetflag ("os"))
  350.     Msg (0, "You can't run screen on a terminal that overstrikes.");
  351.     if (tgetflag ("ns"))
  352.     Msg (0, "Terminal must support scrolling.");
  353.     if (!(CL = tgetstr ("cl", &tp)))
  354.     Msg (0, "Clear screen capability required.");
  355.     if (!(CM = tgetstr ("cm", &tp)))
  356.     Msg (0, "Addressable cursor capability required.");
  357.     if (s = tgetstr ("ps", &tp))
  358.     PC = s[0];
  359.     flowctl = !tgetflag ("NF");
  360.     AM = tgetflag ("am");
  361.     if (tgetflag ("LP"))
  362.     AM = 0;
  363.     TI = tgetstr ("ti", &tp);
  364.     TE = tgetstr ("te", &tp);
  365.     if (!(BL = tgetstr ("bl", &tp)))
  366.     BL = "\007";
  367.     VB = tgetstr ("vb", &tp);
  368.     if (!(BC = tgetstr ("bc", &tp))) {
  369.     if (tgetflag ("bs"))
  370.         BC = "\b";
  371.     else
  372.         BC = tgetstr ("le", &tp);
  373.     }
  374.     if (!(CR = tgetstr ("cr", &tp)))
  375.     CR = "\r";
  376.     if (!(NL = tgetstr ("nl", &tp)))
  377.     NL = "\n";
  378.     IS = tgetstr ("is", &tp);
  379.     if (tgetnum ("sg") <= 0) {
  380.     US = tgetstr ("us", &tp);
  381.     UE = tgetstr ("ue", &tp);
  382.     SO = tgetstr ("so", &tp);
  383.     SE = tgetstr ("se", &tp);
  384.     MB = tgetstr ("mb", &tp);
  385.     MD = tgetstr ("md", &tp);
  386.     MH = tgetstr ("mh", &tp);
  387.     MR = tgetstr ("mr", &tp);
  388.     ME = tgetstr ("me", &tp);
  389.     /*
  390.      * Does ME also reverse the effect of SO and/or US?  This is not
  391.      * clearly specified by the termcap manual.
  392.      * Anyway, we should at least look whether ME and SE/UE are equal:
  393.      */
  394.     if (SE && UE && ME && (strcmp (SE, UE) == 0 || strcmp (ME, UE) == 0))
  395.         UE = 0;
  396.     if (SE && ME && strcmp (SE, ME) == 0)
  397.         SE = 0;
  398.     }
  399.     CE = tgetstr ("ce", &tp);
  400.     CD = tgetstr ("cd", &tp);
  401.     if (!(DO = tgetstr ("do", &tp)))
  402.     DO = NL;
  403.     UP = tgetstr ("up", &tp);
  404.     ND = tgetstr ("nd", &tp);
  405.     SR = tgetstr ("sr", &tp);
  406.     if (!(SF = tgetstr ("sf", &tp)))
  407.     SF = NL;
  408.     AL = tgetstr ("al", &tp);
  409.     DL = tgetstr ("dl", &tp);
  410.     CS = tgetstr ("cs", &tp);
  411.     DC = tgetstr ("dc", &tp);
  412.     IC = tgetstr ("ic", &tp);
  413.     IM = tgetstr ("im", &tp);
  414.     EI = tgetstr ("ei", &tp);
  415.     if (tgetflag ("in"))
  416.     IC = IM = 0;
  417.     if (IC && IC[0] == '\0')
  418.     IC = 0;
  419.     if (IM && IM[0] == '\0')
  420.     IM = 0;
  421.     if (EI && EI[0] == '\0')
  422.     EI = 0;
  423.     KS = tgetstr ("ks", &tp);
  424.     KE = tgetstr ("ke", &tp);
  425.     blank = malloc (cols);
  426.     null = malloc (cols);
  427.     OldImage = malloc (cols);
  428.     OldAttr = malloc (cols);
  429.     if (!(blank && null && OldImage && OldAttr))
  430.     Msg (0, "Out of memory.");
  431.     MakeBlankLine (blank, cols);
  432.     bzero (null, cols);
  433.     UPcost = CalcCost (UP);
  434.     DOcost = CalcCost (DO);
  435.     LEcost = CalcCost (BC);
  436.     NDcost = CalcCost (ND);
  437.     CRcost = CalcCost (CR);
  438.     IMcost = CalcCost (IM);
  439.     EIcost = CalcCost (EI);
  440.     PutStr (IS);
  441.     PutStr (TI);
  442.     PutStr (CL);
  443. }
  444.  
  445. FinitTerm () {
  446.     PutStr (TE);
  447.     PutStr (IS);
  448. }
  449.  
  450. static AddCap (s) char *s; {
  451.     register n;
  452.  
  453.     if (tcLineLen + (n = strlen (s)) > 55) {
  454.     strcat (Termcap, "\\\n\t:");
  455.     tcLineLen = 0;
  456.     }
  457.     strcat (Termcap, s);
  458.     tcLineLen += n;
  459. }
  460.  
  461. char *MakeTermcap (aflag) {
  462.     char buf[1024];
  463.     register char **pp, *p;
  464.  
  465.     strcpy (Termcap, TermcapConst);
  466.     sprintf (buf, "li#%d:co#%d:", rows, cols);
  467.     AddCap (buf);
  468.     if (VB)
  469.     AddCap ("vb=\\E[?5h\\E[?5l:");
  470.     if (US) {
  471.     AddCap ("us=\\E[4m:");
  472.     AddCap ("ue=\\E[24m:");
  473.     }
  474.     if (SO) {
  475.     AddCap ("so=\\E[3m:");
  476.     AddCap ("se=\\E[23m:");
  477.     }
  478.     if (MB)
  479.     AddCap ("mb=\\E[5m:");
  480.     if (MD)
  481.     AddCap ("md=\\E[1m:");
  482.     if (MH)
  483.     AddCap ("mh=\\E[2m:");
  484.     if (MR)
  485.     AddCap ("mr=\\E[7m:");
  486.     if (MB || MD || MH || MR)
  487.     AddCap ("me=\\E[0m:");
  488.     if ((CS && SR) || AL || aflag) {
  489.     AddCap ("sr=\\EM:");
  490.     AddCap ("al=\\E[L:");
  491.     AddCap ("AL=\\E[%dL:");
  492.     }
  493.     if (CS || DL || aflag) {
  494.     AddCap ("dl=\\E[M:");
  495.     AddCap ("DL=\\E[%dM:");
  496.     }
  497.     if (CS)
  498.     AddCap ("cs=\\E[%i%d;%dr:");
  499.     if (DC || aflag) {
  500.     AddCap ("dc=\\E[P:");
  501.     AddCap ("DC=\\E[%dP:");
  502.     }
  503.     if (IC || IM || aflag) {
  504.     AddCap ("im=\\E[4h:");
  505.     AddCap ("ei=\\E[4l:");
  506.     AddCap ("ic=:");
  507.     AddCap ("IC=\\E[%d@:");
  508.     }
  509.     if (KS)
  510.     AddCap ("ks=\\E=:");
  511.     if (KE)
  512.     AddCap ("ke=\\E>:");
  513.     for (pp = KeyCaps; *pp; ++pp)
  514.     if (p = tgetstr (*pp, &tp)) {
  515.         MakeString (*pp, buf, p);
  516.         AddCap (buf);
  517.     }
  518.     return Termcap;
  519. }
  520.  
  521. MakeString (cap, buf, s) char *cap, *buf; register char *s; {
  522.     register char *p = buf;
  523.     register unsigned c;
  524.  
  525.     *p++ = *cap++; *p++ = *cap; *p++ = '=';
  526.     while (c = *s++) {
  527.     switch (c) {
  528.     case '\033':
  529.         *p++ = '\\'; *p++ = 'E'; break;
  530.     case ':':
  531.         sprintf (p, "\\072"); p += 4; break;
  532.     case '^': case '\\':
  533.         *p++ = '\\'; *p++ = c; break;
  534.     default:
  535.         if (c >= 200) {
  536.         sprintf (p, "\\%03o", c & 0377); p += 4;
  537.         } else if (c < ' ') {
  538.         *p++ = '^'; *p++ = c + '@';
  539.         } else *p++ = c;
  540.     }
  541.     }
  542.     *p++ = ':'; *p = '\0';
  543. }
  544.  
  545. Activate (wp) struct win *wp; {
  546.     RemoveStatus (wp);
  547.     curr = wp;
  548.     display = 1;
  549.     NewRendition (GlobalAttr, curr->LocalAttr);
  550.     GlobalAttr = curr->LocalAttr;
  551.     InsertMode (curr->insert);
  552.     KeypadMode (curr->keypad);
  553.     if (CS)
  554.     PutStr (tgoto (CS, curr->bot, curr->top));
  555.     Redisplay ();
  556. }
  557.  
  558. ResetScreen (p) register struct win *p; {
  559.     register i;
  560.  
  561.     bzero (p->tabs, cols);
  562.     for (i = 8; i < cols; i += 8)
  563.     p->tabs[i] = 1;
  564.     p->wrap = 1;
  565.     p->origin = 0;
  566.     p->insert = 0;
  567.     p->vbwait = 0;
  568.     p->keypad = 0;
  569.     p->top = 0;
  570.     p->bot = rows - 1;
  571.     p->saved = 0;
  572.     p->LocalAttr = 0;
  573.     p->x = p->y = 0;
  574.     p->state = LIT;
  575.     p->StringType = NONE;
  576. }
  577.  
  578. WriteString (wp, buf, len) struct win *wp; register char *buf; {
  579.     register c, intermediate = 0;
  580.  
  581.     if (!len)
  582.     return;
  583.     curr = wp;
  584.     display = curr->active;
  585.     if (display)
  586.     RemoveStatus (wp);
  587.     do {
  588.     c = *buf++;
  589.     if (c == '\0' || c == '\177')
  590.         continue;
  591. NextChar:
  592.     switch (curr->state) {
  593.     case TERM:
  594.         switch (c) {
  595.         case '\\':
  596.             curr->state = LIT;
  597.             *(curr->stringp) = '\0';
  598.             if (curr->StringType == PM && display) {
  599.             MakeStatus (curr->string, curr);
  600.             if (status && len > 1) {
  601.                 curr->outlen = len-1;
  602.                 bcopy (buf, curr->outbuf, curr->outlen);
  603.                 return;
  604.             }
  605.             }
  606.             break;
  607.         default:
  608.             curr->state = STR;
  609.             AddChar ('\033');
  610.             AddChar (c);
  611.         }
  612.         break;
  613.     case STR:
  614.         switch (c) {
  615.         case '\0':
  616.             break;
  617.         case '\033':
  618.             curr->state = TERM; break;
  619.         default:
  620.             AddChar (c);
  621.         }
  622.         break;
  623.     case ESC:
  624.         switch (c) {
  625.         case '[':
  626.         NumArgs = 0;
  627.         intermediate = 0;
  628.         bzero ((char *)args, MAXARGS * sizeof (int));
  629.         bzero (GotArg, MAXARGS);
  630.         curr->state = CSI;
  631.         break;
  632.         case ']':
  633.         StartString (OSC); break;
  634.         case '_':
  635.         StartString (APC); break;
  636.         case 'P':
  637.         StartString (DCS); break;
  638.         case '^':
  639.         StartString (PM); break;
  640.         default:
  641.         if (Special (c))
  642.             break;
  643.         if (c >= ' ' && c <= '/') {
  644.             intermediate = intermediate ? -1 : c;
  645.         } else if (c >= '0' && c <= '~') {
  646.             DoESC (c, intermediate);
  647.             curr->state = LIT;
  648.         } else {
  649.             curr->state = LIT;
  650.             goto NextChar;
  651.         }
  652.         }
  653.         break;
  654.     case CSI:
  655.         switch (c) {
  656.         case '0': case '1': case '2': case '3': case '4':
  657.         case '5': case '6': case '7': case '8': case '9':
  658.         if (NumArgs < MAXARGS) {
  659.             args[NumArgs] = 10 * args[NumArgs] + c - '0';
  660.             GotArg[NumArgs] = 1;
  661.         }
  662.         break;
  663.         case ';': case ':':
  664.         NumArgs++; break;
  665.         default:
  666.         if (Special (c))
  667.             break;
  668.         if (c >= '@' && c <= '~') {
  669.             NumArgs++;
  670.             DoCSI (c, intermediate);
  671.             curr->state = LIT;
  672.         } else if ((c >= ' ' && c <= '/') || (c >= '<' && c <= '?')) {
  673.             intermediate = intermediate ? -1 : c;
  674.         } else {
  675.             curr->state = LIT;
  676.             goto NextChar;
  677.         }
  678.         }
  679.         break;
  680.     default:
  681.         if (!Special (c)) {
  682.         if (c == '\033') {
  683.             intermediate = 0;
  684.             curr->state = ESC;
  685.         } else if (c < ' ') {
  686.             break;
  687.         } else if (curr->x < cols-1) {
  688.             if (curr->insert) {
  689.             InsertAChar (c);
  690.             } else {
  691.             if (display)
  692.                 putchar (c);
  693.             SetChar (c);
  694.             }
  695.             curr->x++;
  696.         } else if (curr->x == cols-1) {
  697.             SetChar (c);
  698.             if (!(AM && curr->y == rows-1)) {
  699.             if (display)
  700.                 putchar (c);
  701.             Goto (-1, -1, curr->y, curr->x);
  702.             }
  703.             curr->x++;
  704.         } else {
  705.             if (curr->wrap) {
  706.             Return ();
  707.             LineFeed ();
  708.             if (curr->insert) {
  709.                 InsertAChar (c);
  710.             } else {
  711.                 if (display)
  712.                 putchar (c);
  713.                 SetChar (c);
  714.             }
  715.             curr->x = 1;
  716.             } else curr->x = cols;
  717.         }
  718.         }
  719.     }
  720.     } while (--len);
  721.     curr->outlen = 0;
  722. }
  723.  
  724. static Special (c) register c; {
  725.     switch (c) {
  726.     case '\b':
  727.     BackSpace (); return 1;
  728.     case '\r':
  729.     Return (); return 1;
  730.     case '\n':
  731.     LineFeed (); return 1;
  732.     case '\007':
  733.     PutStr (BL);
  734.     if (!display)
  735.         curr->bell = 1;
  736.     return 1;
  737.     case '\t':
  738.     ForwardTab (); return 1;
  739.     }
  740.     return 0;
  741. }
  742.  
  743. static DoESC (c, intermediate) {
  744.     switch (intermediate) {
  745.     case 0:
  746.     switch (c) {
  747.     case 'E':
  748.         Return ();
  749.         LineFeed ();
  750.         break;
  751.     case 'D':
  752.         LineFeed (); 
  753.         break;
  754.     case 'M':
  755.         ReverseLineFeed ();
  756.         break;
  757.     case 'H':
  758.         curr->tabs[curr->x] = 1;
  759.         break;
  760.     case '7':
  761.         SaveCursor ();
  762.         break;
  763.     case '8':
  764.         RestoreCursor ();
  765.         break;
  766.     case 'c':
  767.         ClearScreen ();
  768.         Goto (curr->y, curr->x, 0, 0);
  769.         NewRendition (GlobalAttr, 0);
  770.         SetRendition (0);
  771.         if (curr->insert)
  772.         InsertMode (0);
  773.         if (curr->keypad)
  774.         KeypadMode (0);
  775.         if (CS)
  776.         PutStr (tgoto (CS, rows-1, 0));
  777.         ResetScreen (curr);
  778.         break;
  779.     case '=':
  780.         KeypadMode (1);
  781.         curr->keypad = 1;
  782.         break;
  783.     case '>':
  784.         KeypadMode (0);
  785.         curr->keypad = 0;
  786.         break;
  787.     }
  788.     break;
  789.     case '#':
  790.     switch (c) {
  791.     case '8':
  792.         FillWithEs ();
  793.         break;
  794.     }
  795.     break;
  796.     }
  797. }
  798.  
  799. static DoCSI (c, intermediate) {
  800.     register i, a1 = args[0], a2 = args[1];
  801.  
  802.     if (NumArgs >= MAXARGS)
  803.     NumArgs = MAXARGS;
  804.     for (i = 0; i < NumArgs; ++i)
  805.     if (args[i] == 0)
  806.         GotArg[i] = 0;
  807.     switch (intermediate) {
  808.     case 0:
  809.     switch (c) {
  810.     case 'H': case 'f':
  811.         if (!GotArg[0]) a1 = 1;
  812.         if (!GotArg[1]) a2 = 1;
  813.         if (curr->origin)
  814.         a1 += curr->top;
  815.         if (a1 < 1)
  816.         a1 = 1;
  817.         if (a1 > rows)
  818.         a1 = rows;
  819.         if (a2 < 1)
  820.         a2 = 1;
  821.         if (a2 > cols)
  822.         a2 = cols;
  823.         a1--; a2--;
  824.         Goto (curr->y, curr->x, a1, a2);
  825.         curr->y = a1;
  826.         curr->x = a2;
  827.         break;
  828.     case 'J':
  829.         if (!GotArg[0] || a1 < 0 || a1 > 2)
  830.         a1 = 0;
  831.         switch (a1) {
  832.         case 0:
  833.         ClearToEOS (); break;
  834.         case 1:
  835.         ClearFromBOS (); break;
  836.         case 2:
  837.         ClearScreen ();
  838.         Goto (0, 0, curr->y, curr->x);
  839.         break;
  840.         }
  841.         break;
  842.     case 'K':
  843.         if (!GotArg[0] || a1 < 0 || a2 > 2)
  844.         a1 = 0;
  845.         switch (a1) {
  846.         case 0:
  847.         ClearToEOL (); break;
  848.         case 1:
  849.         ClearFromBOL (); break;
  850.         case 2:
  851.         ClearLine (); break;
  852.         }
  853.         break;
  854.     case 'A':
  855.         CursorUp (GotArg[0] ? a1 : 1);
  856.         break;
  857.     case 'B':
  858.         CursorDown (GotArg[0] ? a1 : 1);
  859.         break;
  860.     case 'C':
  861.         CursorRight (GotArg[0] ? a1 : 1);
  862.         break;
  863.     case 'D':
  864.         CursorLeft (GotArg[0] ? a1 : 1);
  865.         break;
  866.     case 'm':
  867.         SelectRendition ();
  868.         break;
  869.     case 'g':
  870.         if (!GotArg[0] || a1 == 0)
  871.         curr->tabs[curr->x] = 0;
  872.         else if (a1 == 3)
  873.         bzero (curr->tabs, cols);
  874.         break;
  875.     case 'r':
  876.         if (!CS)
  877.         break;
  878.         if (!GotArg[0]) a1 = 1;
  879.         if (!GotArg[1]) a2 = rows;
  880.         if (a1 < 1 || a2 > rows || a1 >= a2)
  881.         break;
  882.         curr->top = a1-1;
  883.         curr->bot = a2-1;
  884.         PutStr (tgoto (CS, curr->bot, curr->top));
  885.         if (curr->origin) {
  886.         Goto (-1, -1, curr->top, 0);
  887.         curr->y = curr->top;
  888.         curr->x = 0;
  889.         } else {
  890.         Goto (-1, -1, 0, 0);
  891.         curr->y = curr->x = 0;
  892.         }
  893.         break;
  894.     case 'I':
  895.         if (!GotArg[0]) a1 = 1;
  896.         while (a1--)
  897.         ForwardTab ();
  898.         break;
  899.     case 'Z':
  900.         if (!GotArg[0]) a1 = 1;
  901.         while (a1--)
  902.         BackwardTab ();
  903.         break;
  904.     case 'L':
  905.         InsertLine (GotArg[0] ? a1 : 1);
  906.         break;
  907.     case 'M':
  908.         DeleteLine (GotArg[0] ? a1 : 1);
  909.         break;
  910.     case 'P':
  911.         DeleteChar (GotArg[0] ? a1 : 1);
  912.         break;
  913.     case '@':
  914.         InsertChar (GotArg[0] ? a1 : 1);
  915.         break;
  916.     case 'h':
  917.         SetMode (1);
  918.         break;
  919.     case 'l':
  920.         SetMode (0);
  921.         break;
  922.     }
  923.     break;
  924.     case '?':
  925.     if (c != 'h' && c != 'l')
  926.         break;
  927.     if (!GotArg[0])
  928.         break;
  929.     i = (c == 'h');
  930.     if (a1 == 5) {
  931.         if (i) {
  932.         curr->vbwait = 1;
  933.         } else {
  934.         if (curr->vbwait)
  935.             PutStr (VB);
  936.         curr->vbwait = 0;
  937.         }
  938.     } else if (a1 == 6) {
  939.         curr->origin = i;
  940.         if (curr->origin) {
  941.         Goto (curr->y, curr->x, curr->top, 0);
  942.         curr->y = curr->top;
  943.         curr->x = 0;
  944.         } else {
  945.         Goto (curr->y, curr->x, 0, 0);
  946.         curr->y = curr->x = 0;
  947.         }
  948.     } else if (a1 == 7) {
  949.         curr->wrap = i;
  950.     }
  951.     break;
  952.     }
  953. }
  954.  
  955. static PutChar (c) {
  956.     putchar (c);
  957. }
  958.  
  959. static PutStr (s) char *s; {
  960.     if (display && s)
  961.     tputs (s, 1, PutChar);
  962. }
  963.  
  964. static SetChar (c) register c; {
  965.     register struct win *p = curr;
  966.  
  967.     p->image[p->y][p->x] = c;
  968.     p->attr[p->y][p->x] = p->LocalAttr;
  969. }
  970.  
  971. static StartString (type) enum string_t type; {
  972.     curr->StringType = type;
  973.     curr->stringp = curr->string;
  974.     curr->state = STR;
  975. }
  976.  
  977. static AddChar (c) {
  978.     if (curr->stringp > curr->string+MAXSTR-1)
  979.     curr->state = LIT;
  980.     else
  981.     *(curr->stringp)++ = c;
  982. }
  983.  
  984. /* Insert mode is a toggle on some terminals, so we need this hack:
  985.  */
  986. InsertMode (on) {
  987.     if (on) {
  988.     if (!insert)
  989.         PutStr (IM);
  990.     } else if (insert)
  991.     PutStr (EI);
  992.     insert = on;
  993. }
  994.  
  995. /* ...and maybe keypad application mode is a toggle, too:
  996.  */
  997. KeypadMode (on) {
  998.     if (on) {
  999.     if (!keypad)
  1000.         PutStr (KS);
  1001.     } else if (keypad)
  1002.     PutStr (KE);
  1003.     keypad = on;
  1004. }
  1005.  
  1006. static SaveCursor () {
  1007.     curr->saved = 1;
  1008.     curr->Saved_x = curr->x;
  1009.     curr->Saved_y = curr->y;
  1010.     curr->SavedLocalAttr = curr->LocalAttr;
  1011. }
  1012.  
  1013. static RestoreCursor () {
  1014.     if (curr->saved) {
  1015.     curr->LocalAttr = curr->SavedLocalAttr;
  1016.     NewRendition (GlobalAttr, curr->LocalAttr);
  1017.     GlobalAttr = curr->LocalAttr;
  1018.     Goto (curr->y, curr->x, curr->Saved_y, curr->Saved_x);
  1019.     curr->x = curr->Saved_x;
  1020.     curr->y = curr->Saved_y;
  1021.     }
  1022. }
  1023.  
  1024. /*ARGSUSED*/
  1025. CountChars (c) {
  1026.     StrCost++;
  1027. }
  1028.  
  1029. CalcCost (s) register char *s; {
  1030.     if (s) {
  1031.     StrCost = 0;
  1032.     tputs (s, 1, CountChars);
  1033.     return StrCost;
  1034.     } else return EXPENSIVE;
  1035. }
  1036.  
  1037. static Goto (y1, x1, y2, x2) {
  1038.     register dy, dx;
  1039.     register cost = 0;
  1040.     register char *s;
  1041.     int CMcost, n, m;
  1042.     enum move_t xm = M_NONE, ym = M_NONE;
  1043.  
  1044.     if (!display)
  1045.     return;
  1046.     if (x1 == cols || x2 == cols) {
  1047.     if (x2 == cols) --x2;
  1048.     goto DoCM;
  1049.     }
  1050.     dx = x2 - x1;
  1051.     dy = y2 - y1;
  1052.     if (dy == 0 && dx == 0)
  1053.     return;
  1054.     if (y1 == -1 || x1 == -1) {
  1055. DoCM:
  1056.     PutStr (tgoto (CM, x2, y2));
  1057.     return;
  1058.     }
  1059.     CMcost = CalcCost (tgoto (CM, x2, y2));
  1060.     if (dx > 0) {
  1061.     if ((n = RewriteCost (y1, x1, x2)) < (m = dx * NDcost)) {
  1062.         cost = n;
  1063.         xm = M_RW;
  1064.     } else {
  1065.         cost = m;
  1066.         xm = M_RI;
  1067.     }
  1068.     } else if (dx < 0) {
  1069.     cost = -dx * LEcost;
  1070.     xm = M_LE;
  1071.     }
  1072.     if (dx && (n = RewriteCost (y1, 0, x2) + CRcost) < cost) {
  1073.     cost = n;
  1074.     xm = M_CR;
  1075.     }
  1076.     if (cost >= CMcost)
  1077.     goto DoCM;
  1078.     if (dy > 0) {
  1079.     cost += dy * DOcost;
  1080.     ym = M_DO;
  1081.     } else if (dy < 0) {
  1082.     cost += -dy * UPcost;
  1083.     ym = M_UP;
  1084.     }
  1085.     if (cost >= CMcost)
  1086.     goto DoCM;
  1087.     if (xm != M_NONE) {
  1088.     if (xm == M_LE || xm == M_RI) {
  1089.         if (xm == M_LE) {
  1090.         s = BC; dx = -dx;
  1091.         } else s = ND;
  1092.         while (dx-- > 0)
  1093.         PutStr (s);
  1094.     } else {
  1095.         if (xm == M_CR) {
  1096.         PutStr (CR);
  1097.         x1 = 0;
  1098.         }
  1099.         if (x1 < x2) {
  1100.         if (curr->insert)
  1101.             InsertMode (0);
  1102.         for (s = curr->image[y1]+x1; x1 < x2; x1++, s++)
  1103.             putchar (*s);
  1104.         if (curr->insert)
  1105.             InsertMode (1);
  1106.         }
  1107.     }
  1108.     }
  1109.     if (ym != M_NONE) {
  1110.     if (ym == M_UP) {
  1111.         s = UP; dy = -dy;
  1112.     } else s = DO;
  1113.     while (dy-- > 0)
  1114.         PutStr (s);
  1115.     }
  1116. }
  1117.  
  1118. static RewriteCost (y, x1, x2) {
  1119.     register cost, dx;
  1120.     register char *p = curr->attr[y]+x1;
  1121.  
  1122.     if (AM && y == rows-1 && x2 == cols-1)
  1123.     return EXPENSIVE;
  1124.     cost = dx = x2 - x1;
  1125.     if (dx == 0)
  1126.     return 0;
  1127.     if (curr->insert)
  1128.     cost += EIcost + IMcost;
  1129.     do {
  1130.     if (*p++ != GlobalAttr)
  1131.         return EXPENSIVE;
  1132.     } while (--dx);
  1133.     return cost;
  1134. }
  1135.  
  1136. static BackSpace () {
  1137.     if (curr->x > 0) {
  1138.     if (curr->x < cols) {
  1139.         if (BC)
  1140.         PutStr (BC);
  1141.         else
  1142.         Goto (curr->y, curr->x, curr->y, curr->x-1);
  1143.     }
  1144.     curr->x--;
  1145.     }
  1146. }
  1147.  
  1148. static Return () {
  1149.     if (curr->x > 0) {
  1150.     curr->x = 0;
  1151.     PutStr (CR);
  1152.     }
  1153. }
  1154.  
  1155. static LineFeed () {
  1156.     if (curr->y == curr->bot) {
  1157.     ScrollUpMap (curr->image);
  1158.     ScrollUpMap (curr->attr);
  1159.     } else if (curr->y < rows-1) {
  1160.     curr->y++;
  1161.     }
  1162.     PutStr (NL);
  1163. }
  1164.  
  1165. static ReverseLineFeed () {
  1166.     if (curr->y == curr->top) {
  1167.     ScrollDownMap (curr->image);
  1168.     ScrollDownMap (curr->attr);
  1169.     if (SR) {
  1170.         PutStr (SR);
  1171.     } else if (AL) {
  1172.         Goto (curr->top, curr->x, curr->top, 0);
  1173.         PutStr (AL);
  1174.         Goto (curr->top, 0, curr->top, curr->x);
  1175.     } else Redisplay ();
  1176.     } else if (curr->y > 0) {
  1177.     CursorUp (1);
  1178.     }
  1179. }
  1180.  
  1181. static InsertAChar (c) {
  1182.     register y = curr->y, x = curr->x;
  1183.  
  1184.     if (x == cols)
  1185.     x--;
  1186.     bcopy (curr->image[y], OldImage, cols);
  1187.     bcopy (curr->attr[y], OldAttr, cols);
  1188.     bcopy (curr->image[y]+x, curr->image[y]+x+1, cols-x-1);
  1189.     bcopy (curr->attr[y]+x, curr->attr[y]+x+1, cols-x-1);
  1190.     SetChar (c);
  1191.     if (!display)
  1192.     return;
  1193.     if (IC || IM) {
  1194.     if (!curr->insert)
  1195.         InsertMode (1);
  1196.     PutStr (IC);
  1197.     putchar (c);
  1198.     if (!curr->insert)
  1199.         InsertMode (0);
  1200.     } else {
  1201.     RedisplayLine (OldImage, OldAttr, y, x, cols-1);
  1202.     ++x;
  1203.     Goto (y, last_x, y, x);
  1204.     }
  1205. }
  1206.  
  1207. static InsertChar (n) {
  1208.     register i, y = curr->y, x = curr->x;
  1209.  
  1210.     if (x == cols)
  1211.     return;
  1212.     bcopy (curr->image[y], OldImage, cols);
  1213.     bcopy (curr->attr[y], OldAttr, cols);
  1214.     if (n > cols-x)
  1215.     n = cols-x;
  1216.     bcopy (curr->image[y]+x, curr->image[y]+x+n, cols-x-n);
  1217.     bcopy (curr->attr[y]+x, curr->attr[y]+x+n, cols-x-n);
  1218.     ClearInLine (0, y, x, x+n-1);
  1219.     if (!display)
  1220.     return;
  1221.     if (IC || IM) {
  1222.     if (!curr->insert)
  1223.         InsertMode (1);
  1224.     for (i = n; i; i--) {
  1225.         PutStr (IC);
  1226.         putchar (' ');
  1227.     }
  1228.     if (!curr->insert)
  1229.         InsertMode (0);
  1230.     Goto (y, x+n, y, x);
  1231.     } else {
  1232.     RedisplayLine (OldImage, OldAttr, y, x, cols-1);
  1233.     Goto (y, last_x, y, x);
  1234.     }
  1235. }
  1236.  
  1237. static DeleteChar (n) {
  1238.     register i, y = curr->y, x = curr->x;
  1239.  
  1240.     if (x == cols)
  1241.     return;
  1242.     bcopy (curr->image[y], OldImage, cols);
  1243.     bcopy (curr->attr[y], OldAttr, cols);
  1244.     if (n > cols-x)
  1245.     n = cols-x;
  1246.     bcopy (curr->image[y]+x+n, curr->image[y]+x, cols-x-n);
  1247.     bcopy (curr->attr[y]+x+n, curr->attr[y]+x, cols-x-n);
  1248.     ClearInLine (0, y, cols-n, cols-1);
  1249.     if (!display)
  1250.     return;
  1251.     if (DC) {
  1252.     for (i = n; i; i--)
  1253.         PutStr (DC);
  1254.     } else {
  1255.     RedisplayLine (OldImage, OldAttr, y, x, cols-1);
  1256.     Goto (y, last_x, y, x);
  1257.     }
  1258. }
  1259.  
  1260. static DeleteLine (n) {
  1261.     register i, old = curr->top;
  1262.  
  1263.     if (n > curr->bot-curr->y+1)
  1264.     n = curr->bot-curr->y+1;
  1265.     curr->top = curr->y;
  1266.     for (i = n; i; i--) {
  1267.     ScrollUpMap (curr->image);
  1268.     ScrollUpMap (curr->attr);
  1269.     }
  1270.     if (DL) {
  1271.     Goto (curr->y, curr->x, curr->y, 0);
  1272.     for (i = n; i; i--)
  1273.         PutStr (DL);
  1274.     Goto (curr->y, 0, curr->y, curr->x);
  1275.     } else if (CS) {
  1276.     PutStr (tgoto (CS, curr->bot, curr->top));
  1277.     Goto (-1, -1, curr->bot, 0);
  1278.     for (i = n; i; i--)
  1279.         PutStr (SF);
  1280.     PutStr (tgoto (CS, curr->bot, old));
  1281.     Goto (-1, -1, curr->y, curr->x);
  1282.     } else Redisplay ();
  1283.     curr->top = old;
  1284. }
  1285.  
  1286. static InsertLine (n) {
  1287.     register i, old = curr->top;
  1288.  
  1289.     if (n > curr->bot-curr->y+1)
  1290.     n = curr->bot-curr->y+1;
  1291.     curr->top = curr->y;
  1292.     for (i = n; i; i--) {
  1293.     ScrollDownMap (curr->image);
  1294.     ScrollDownMap (curr->attr);
  1295.     }
  1296.     if (AL) {
  1297.     Goto (curr->y, curr->x, curr->y, 0);
  1298.     for (i = n; i; i--)
  1299.         PutStr (AL);
  1300.     Goto (curr->y, 0, curr->y, curr->x);
  1301.     } else if (CS && SR) {
  1302.     PutStr (tgoto (CS, curr->bot, curr->top));
  1303.     Goto (-1, -1, curr->y, 0);
  1304.     for (i = n; i; i--)
  1305.         PutStr (SR);
  1306.     PutStr (tgoto (CS, curr->bot, old));
  1307.     Goto (-1, -1, curr->y, curr->x);
  1308.     } else Redisplay ();
  1309.     curr->top = old;
  1310. }
  1311.  
  1312. static ScrollUpMap (pp) char **pp; {
  1313.     register char *tmp = pp[curr->top];
  1314.  
  1315.     bcopy (pp+curr->top+1, pp+curr->top,
  1316.     (curr->bot-curr->top) * sizeof (char *));
  1317.     if (pp == curr->image)
  1318.     bclear (tmp, cols);
  1319.     else
  1320.     bzero (tmp, cols);
  1321.     pp[curr->bot] = tmp;
  1322. }
  1323.  
  1324. static ScrollDownMap (pp) char **pp; {
  1325.     register char *tmp = pp[curr->bot];
  1326.  
  1327.     bcopy (pp+curr->top, pp+curr->top+1,
  1328.     (curr->bot-curr->top) * sizeof (char *));
  1329.     if (pp == curr->image)
  1330.     bclear (tmp, cols);
  1331.     else
  1332.     bzero (tmp, cols);
  1333.     pp[curr->top] = tmp;
  1334. }
  1335.  
  1336. static ForwardTab () {
  1337.     register x = curr->x;
  1338.  
  1339.     if (curr->tabs[x] && x < cols-1)
  1340.     ++x;
  1341.     while (x < cols-1 && !curr->tabs[x])
  1342.     x++;
  1343.     Goto (curr->y, curr->x, curr->y, x);
  1344.     curr->x = x;
  1345. }
  1346.  
  1347. static BackwardTab () {
  1348.     register x = curr->x;
  1349.  
  1350.     if (curr->tabs[x] && x > 0)
  1351.     x--;
  1352.     while (x > 0 && !curr->tabs[x])
  1353.     x--;
  1354.     Goto (curr->y, curr->x, curr->y, x);
  1355.     curr->x = x;
  1356. }
  1357.  
  1358. static ClearScreen () {
  1359.     register i;
  1360.  
  1361.     PutStr (CL);
  1362.     for (i = 0; i < rows; ++i) {
  1363.     bclear (curr->image[i], cols);
  1364.     bzero (curr->attr[i], cols);
  1365.     }
  1366. }
  1367.  
  1368. static ClearFromBOS () {
  1369.     register n, y = curr->y, x = curr->x;
  1370.  
  1371.     for (n = 0; n < y; ++n)
  1372.     ClearInLine (1, n, 0, cols-1);
  1373.     ClearInLine (1, y, 0, x);
  1374.     Goto (curr->y, curr->x, y, x);
  1375.     curr->y = y; curr->x = x;
  1376. }
  1377.  
  1378. static ClearToEOS () {
  1379.     register n, y = curr->y, x = curr->x;
  1380.  
  1381.     if (CD)
  1382.     PutStr (CD);
  1383.     ClearInLine (!CD, y, x, cols-1);
  1384.     for (n = y+1; n < rows; n++)
  1385.     ClearInLine (!CD, n, 0, cols-1);
  1386.     Goto (curr->y, curr->x, y, x);
  1387.     curr->y = y; curr->x = x;
  1388. }
  1389.  
  1390. static ClearLine () {
  1391.     register y = curr->y, x = curr->x;
  1392.  
  1393.     ClearInLine (1, y, 0, cols-1);
  1394.     Goto (curr->y, curr->x, y, x);
  1395.     curr->y = y; curr->x = x;
  1396. }
  1397.  
  1398. static ClearToEOL () {
  1399.     register y = curr->y, x = curr->x;
  1400.  
  1401.     ClearInLine (1, y, x, cols-1);
  1402.     Goto (curr->y, curr->x, y, x);
  1403.     curr->y = y; curr->x = x;
  1404. }
  1405.  
  1406. static ClearFromBOL () {
  1407.     register y = curr->y, x = curr->x;
  1408.  
  1409.     ClearInLine (1, y, 0, x);
  1410.     Goto (curr->y, curr->x, y, x);
  1411.     curr->y = y; curr->x = x;
  1412. }
  1413.  
  1414. static ClearInLine (displ, y, x1, x2) {
  1415.     register i, n;
  1416.  
  1417.     if (x1 == cols) x1--;
  1418.     if (x2 == cols) x2--;
  1419.     if (n = x2 - x1 + 1) {
  1420.     bclear (curr->image[y]+x1, n);
  1421.     bzero (curr->attr[y]+x1, n);
  1422.     if (displ && display) {
  1423.         if (x2 == cols-1 && CE) {
  1424.         Goto (curr->y, curr->x, y, x1);
  1425.         curr->y = y; curr->x = x1;
  1426.         PutStr (CE);
  1427.         return;
  1428.         }
  1429.         if (y == rows-1 && AM)
  1430.         --n;
  1431.         if (n == 0)
  1432.         return;
  1433.         SaveAttr ();
  1434.         Goto (curr->y, curr->x, y, x1);
  1435.         for (i = n; i > 0; i--)
  1436.         putchar (' ');
  1437.         curr->y = y; curr->x = x1 + n;
  1438.         RestoreAttr ();
  1439.     }
  1440.     }
  1441. }
  1442.  
  1443. static CursorRight (n) register n; {
  1444.     register x = curr->x;
  1445.  
  1446.     if (x == cols)
  1447.     return;
  1448.     if ((curr->x += n) >= cols)
  1449.     curr->x = cols-1;
  1450.     Goto (curr->y, x, curr->y, curr->x);
  1451. }
  1452.  
  1453. static CursorUp (n) register n; {
  1454.     register y = curr->y;
  1455.  
  1456.     if ((curr->y -= n) < curr->top)
  1457.     curr->y = curr->top;
  1458.     Goto (y, curr->x, curr->y, curr->x);
  1459. }
  1460.  
  1461. static CursorDown (n) register n; {
  1462.     register y = curr->y;
  1463.  
  1464.     if ((curr->y += n) > curr->bot)
  1465.     curr->y = curr->bot;
  1466.     Goto (y, curr->x, curr->y, curr->x);
  1467. }
  1468.  
  1469. static CursorLeft (n) register n; {
  1470.     register x = curr->x;
  1471.  
  1472.     if ((curr->x -= n) < 0)
  1473.     curr->x = 0;
  1474.     Goto (curr->y, x, curr->y, curr->x);
  1475. }
  1476.  
  1477. static SetMode (on) {
  1478.     register i;
  1479.  
  1480.     for (i = 0; i < NumArgs; ++i) {
  1481.     switch (args[i]) {
  1482.     case 4:
  1483.         curr->insert = on;
  1484.         InsertMode (on);
  1485.         break;
  1486.     }
  1487.     }
  1488. }
  1489.  
  1490. static SelectRendition () {
  1491.     register i, old = GlobalAttr;
  1492.  
  1493.     if (NumArgs == 0)
  1494.     SetRendition (0);
  1495.     else for (i = 0; i < NumArgs; ++i)
  1496.     SetRendition (args[i]);
  1497.     NewRendition (old, GlobalAttr);
  1498. }
  1499.  
  1500. static SetRendition (n) register n; {
  1501.     switch (n) {
  1502.     case 0:
  1503.     GlobalAttr = 0; break;
  1504.     case 1:
  1505.     GlobalAttr |= A_BD; break;
  1506.     case 2:
  1507.     GlobalAttr |= A_DI; break;
  1508.     case 3:
  1509.     GlobalAttr |= A_SO; break;
  1510.     case 4:
  1511.     GlobalAttr |= A_US; break;
  1512.     case 5:
  1513.     GlobalAttr |= A_BL; break;
  1514.     case 7:
  1515.     GlobalAttr |= A_RV; break;
  1516.     case 22:
  1517.     GlobalAttr &= ~(A_BD|A_SO|A_DI); break;
  1518.     case 23:
  1519.     GlobalAttr &= ~A_SO; break;
  1520.     case 24:
  1521.     GlobalAttr &= ~A_US; break;
  1522.     case 25:
  1523.     GlobalAttr &= ~A_BL; break;
  1524.     case 27:
  1525.     GlobalAttr &= ~A_RV; break;
  1526.     }
  1527.     curr->LocalAttr = GlobalAttr;
  1528. }
  1529.  
  1530. static NewRendition (old, new) register old, new; {
  1531.     register i;
  1532.  
  1533.     if (old == new)
  1534.     return;
  1535.     for (i = 1; i <= A_MAX; i <<= 1) {
  1536.     if ((old & i) && !(new & i)) {
  1537.         PutStr (UE);
  1538.         PutStr (SE);
  1539.         PutStr (ME);
  1540.         if (new & A_US) PutStr (US);
  1541.         if (new & A_SO) PutStr (SO);
  1542.         if (new & A_BL) PutStr (MB);
  1543.         if (new & A_BD) PutStr (MD);
  1544.         if (new & A_DI) PutStr (MH);
  1545.         if (new & A_RV) PutStr (MR);
  1546.         return;
  1547.     }
  1548.     }
  1549.     if ((new & A_US) && !(old & A_US))
  1550.     PutStr (US);
  1551.     if ((new & A_SO) && !(old & A_SO))
  1552.     PutStr (SO);
  1553.     if ((new & A_BL) && !(old & A_BL))
  1554.     PutStr (MB);
  1555.     if ((new & A_BD) && !(old & A_BD))
  1556.     PutStr (MD);
  1557.     if ((new & A_DI) && !(old & A_DI))
  1558.     PutStr (MH);
  1559.     if ((new & A_RV) && !(old & A_RV))
  1560.     PutStr (MR);
  1561. }
  1562.  
  1563. static SaveAttr () {
  1564.     NewRendition (GlobalAttr, 0);
  1565.     if (curr->insert)
  1566.     InsertMode (0);
  1567. }
  1568.  
  1569. static RestoreAttr () {
  1570.     NewRendition (0, GlobalAttr);
  1571.     if (curr->insert)
  1572.     InsertMode (1);
  1573. }
  1574.  
  1575. static FillWithEs () {
  1576.     register i;
  1577.     register char *p, *ep;
  1578.  
  1579.     curr->y = curr->x = 0;
  1580.     NewRendition (GlobalAttr, 0);
  1581.     SetRendition (0);
  1582.     for (i = 0; i < rows; ++i) {
  1583.     bzero (curr->attr[i], cols);
  1584.     p = curr->image[i];
  1585.     ep = p + cols;
  1586.     for ( ; p < ep; ++p)
  1587.         *p = 'E';
  1588.     }
  1589.     Redisplay ();
  1590. }
  1591.  
  1592. static Redisplay () {
  1593.     register i;
  1594.  
  1595.     PutStr (CL);
  1596.     TmpAttr = GlobalAttr;
  1597.     last_x = last_y = 0;
  1598.     for (i = 0; i < rows; ++i)
  1599.     DisplayLine (blank, null, curr->image[i], curr->attr[i], i, 0, cols-1);
  1600.     NewRendition (TmpAttr, GlobalAttr);
  1601.     Goto (last_y, last_x, curr->y, curr->x);
  1602. }
  1603.  
  1604. static DisplayLine (os, oa, s, as, y, from, to)
  1605.     register char *os, *oa, *s, *as; {
  1606.     register i, x, a;
  1607.  
  1608.     if (to == cols)
  1609.     --to;
  1610.     if (AM && y == rows-1 && to == cols-1)
  1611.     --to;
  1612.     a = TmpAttr;
  1613.     for (x = i = from; i <= to; ++i, ++x) {
  1614.     if (s[i] == os[i] && as[i] == oa[i] && as[i] == a)
  1615.         continue;
  1616.     Goto (last_y, last_x, y, x);
  1617.     last_y = y;
  1618.     last_x = x;
  1619.     if ((a = as[i]) != TmpAttr) {
  1620.         NewRendition (TmpAttr, a);
  1621.         TmpAttr = a;
  1622.     }
  1623.     putchar (s[i]);
  1624.     last_x++;
  1625.     }
  1626. }
  1627.  
  1628. static RedisplayLine (os, oa, y, from, to) char *os, *oa; {
  1629.     if (curr->insert)
  1630.     InsertMode (0);
  1631.     NewRendition (GlobalAttr, 0);
  1632.     TmpAttr = 0;
  1633.     last_y = y;
  1634.     last_x = from;
  1635.     DisplayLine (os, oa, curr->image[y], curr->attr[y], y, from, to);
  1636.     NewRendition (TmpAttr, GlobalAttr);
  1637.     if (curr->insert)
  1638.     InsertMode (1);
  1639. }
  1640.  
  1641. static MakeBlankLine (p, n) register char *p; register n; {
  1642.     do *p++ = ' ';
  1643.     while (--n);
  1644. }
  1645.  
  1646. MakeStatus (msg, wp) char *msg; struct win *wp; {
  1647.     struct win *ocurr = curr;
  1648.     int odisplay = display;
  1649.     register char *s, *t;
  1650.     register max = AM ? cols-1 : cols;
  1651.  
  1652.     for (s = t = msg; *s && t - msg < max; ++s)
  1653.     if (*s >= ' ' && *s <= '~')
  1654.         *t++ = *s;
  1655.     *t = '\0';
  1656.     curr = wp;
  1657.     display = 1;
  1658.     if (status) {
  1659.     if (time ((time_t *)0) - TimeDisplayed < 2)
  1660.         sleep (1);
  1661.     RemoveStatus (wp);
  1662.     }
  1663.     if (t > msg) {
  1664.     status = 1;
  1665.     StatLen = t - msg;
  1666.     Goto (curr->y, curr->x, rows-1, 0);
  1667.     NewRendition (GlobalAttr, A_SO);
  1668.     if (curr->insert)
  1669.         InsertMode (0);
  1670.     printf ("%s", msg);
  1671.     if (curr->insert)
  1672.         InsertMode (1);
  1673.     NewRendition (A_SO, GlobalAttr);
  1674.     fflush (stdout);
  1675.     time (&TimeDisplayed);
  1676.     }
  1677.     curr = ocurr;
  1678.     display = odisplay;
  1679. }
  1680.  
  1681. RemoveStatus (p) struct win *p; {
  1682.     struct win *ocurr = curr;
  1683.     int odisplay = display;
  1684.  
  1685.     if (!status)
  1686.     return;
  1687.     status = 0;
  1688.     curr = p;
  1689.     display = 1;
  1690.     Goto (-1, -1, rows-1, 0);
  1691.     RedisplayLine (null, null, rows-1, 0, StatLen);
  1692.     Goto (rows-1, last_x, curr->y, curr->x);
  1693.     curr = ocurr;
  1694.     display = odisplay;
  1695. }
  1696. SHAR_EOF
  1697. if test 30215 -ne "`wc -c < 'ansi.c'`"
  1698. then
  1699.     echo shar: error transmitting "'ansi.c'" '(should have been 30215 characters)'
  1700. fi
  1701. fi # end of overwriting check
  1702. #    End of shell archive
  1703. exit 0
  1704.  
  1705. -- 
  1706.  
  1707. Rich $alz            "Anger is an energy"
  1708. Cronus Project, BBN Labs    rsalz@bbn.com
  1709. Moderator, comp.sources.unix    sources@uunet.uu.net
  1710.